#include "queue.hpp"
#include "critical.hpp"

using namespace qOS;

//==============================================================================
bool queue::setup(void *pData, const size_t size, const size_t count) noexcept
{
    bool retValue = false;

    if ((nullptr != pData) && (size > 0U) && (count > 0U)) {
        itemsCount = count;
        itemSize = size;
        /*cstat -CERT-EXP36-C_b*/
        head = static_cast<uint8_t*>(pData);
        /*cstat +CERT-EXP36-C_b*/
        reset();
        retValue = true;
    }

    return retValue;
}
//==============================================================================
void queue::reset(void) noexcept
{
    critical::scope {
        /*cstat -CERT-INT30-C_a*/
        tail = head + (itemsCount*itemSize);
        itemsWaiting = 0U;
        writer = head;
        reader = head + ((itemsCount - 1U)*itemSize);
        /*cstat +CERT-INT30-C_a*/
    }
}
//==============================================================================
bool queue::isEmpty(void) const noexcept
{
    return (0U == itemsWaiting);
}
//==============================================================================
bool queue::isFull(void) const noexcept
{
    return (itemsWaiting == itemsCount);
}
//==============================================================================
size_t queue::count(void) const noexcept
{
    return itemsWaiting;
}
//==============================================================================
size_t queue::itemsAvailable(void) const noexcept
{
    return itemsCount - itemsWaiting;
}
//==============================================================================
void queue::moveReader(void) noexcept
{
    reader += itemSize;
    if (reader >= tail) {
        reader = head;
    }
}
//==============================================================================
bool queue::removeFront(void) noexcept
{
    bool retValue = false;

    critical::scope {
        size_t waiting;

        waiting = itemsWaiting;
        if (waiting > 0U) {
            moveReader();
            itemsWaiting = itemsWaiting - 1U;  /* --itemsWaiting */
            retValue = true;
        }
    }

    return retValue;
}
//==============================================================================
void queue::copyDataFromQueue(void * const dst) noexcept
{
    moveReader();
    (void)memcpy(static_cast<void*>(dst), static_cast<void*>(reader), itemSize);
}
//==============================================================================
bool queue::receive(void *dst) noexcept
{
    bool retValue = false;

    critical::scope {
        size_t waiting;

        waiting = itemsWaiting;
        if (waiting > 0U) {
            copyDataFromQueue(dst);
            itemsWaiting = itemsWaiting - 1U;  /* --itemsWaiting */
            retValue = true;
        }
    }
    return retValue;
}
//==============================================================================
void queue::copyDataToQueue(const void *itemToQueue, const queueSendMode xPosition) noexcept
{
    if (queueSendMode::TO_BACK == xPosition) {
        (void)memcpy(static_cast<void*>(writer), itemToQueue, itemSize);
        writer += itemSize;
        if (writer >= tail) {
            writer = head;
        }
    }
    else {
        (void)memcpy(static_cast<void*>(reader), itemToQueue, itemSize);
        reader -= itemSize;
        if (reader < head) {
            reader = (tail - itemSize);
        }
    }
    itemsWaiting = itemsWaiting + 1U;  /* i++temsWaiting */
}
//==============================================================================
bool queue::send(void *itemToQueue, const queueSendMode pos) noexcept
{
    bool retValue = false;

    if ((queueSendMode::TO_BACK == pos) || (queueSendMode::TO_FRONT == pos)) {
        critical::scope {
            if (itemsWaiting < itemsCount) {
                copyDataToQueue(itemToQueue, pos);
                retValue = true;
            }
        }
    }

    return retValue;
}
//==============================================================================
void* queue::peek(void) const noexcept
{
    uint8_t *retValue = nullptr;
    critical::scope {
        size_t waiting;

        waiting = itemsWaiting;
        if (waiting > 0U) {
            retValue = static_cast<uint8_t *>(reader + itemSize);
            if (retValue >= tail) {
                retValue = head;
            }
        }
    }
    return static_cast<void*>(retValue);
}
//==============================================================================
bool queue::isInitialized(void) const noexcept
{
    return (nullptr != head);
}
//==============================================================================
size_t queue::getItemSize(void) const noexcept
{
    return itemSize;
}
//==============================================================================
